package org.lora.biz.workflow.engines.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.bson.Document;
import org.lora.biz.workflow.WorkflowConstant;
import org.lora.biz.workflow.engines.IProcessDefinitionService;
import org.lora.biz.workflow.engines.bean.ActivityDefinitionBean;
import org.lora.biz.workflow.engines.bean.PathDefinitionBean;
import org.lora.biz.workflow.engines.bean.ProcessDefinitionBean;
import org.lora.biz.workflow.engines.entity.ProcessDefVer;
import org.lora.biz.workflow.engines.entity.ProcessDefVerExample;
import org.lora.biz.workflow.engines.entity.ProcessDefinition;
import org.lora.biz.workflow.engines.entity.ProcessDefinitionExample;
import org.lora.biz.workflow.engines.mapper.ProcessDefVerMapper;
import org.lora.biz.workflow.engines.mapper.ProcessDefinitionMapper;
import org.lora.biz.workflow.exception.LoraWorkflowException;
import org.lora.core.annotation.AutoAssemble;
import org.lora.core.annotation.Service;
import org.lora.core.mongodb.IMongoDAO;
import org.lora.core.mongodb.MongoParamEntity;
import org.lora.exception.LoraCoreException;
import org.lora.exception.MongoDBException;
import org.lora.util.StringUtil;
import org.lora.util.ValidateUtil;
import org.lora.webframework.common.FrameWorkBaseService;

import com.alibaba.fastjson.JSONObject;

/**
 * <p>
 * 类名:ProcessDefinitionServiceImpl
 * </p>
 * 描述:流程定义服务实现<br>
 * 创建时间:2016年4月12日 上午11:02:12<br>
 * 
 * @author Bladnir@outlook.com<br>
 *         修改历史:<br>
 *         2016年4月12日 Bladnir 创建<br>
 */
@Service
public class ProcessDefinitionServiceImpl extends FrameWorkBaseService implements IProcessDefinitionService {

	@AutoAssemble
	private IMongoDAO mongoDAO;

	@AutoAssemble
	private ProcessDefinitionMapper processDefinitionMapper;

	@AutoAssemble
	private ProcessDefVerMapper processDefVerMapper;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.lora.biz.workflow.engines.IProcessDefinitionService#saveFlowResourceData
	 * (java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public void saveFlowResourceData(String version, String processDefinitionId, String resourceId, String flowData)
			throws LoraCoreException {

		if (StringUtil.isNull(resourceId)) {// 计算新的resourceId 并更新 版本表

			ValidateUtil.isNull(version, "version");
			ValidateUtil.isNull(processDefinitionId, "processDefinitionId");

			ProcessDefVerExample processDefVerExample = new ProcessDefVerExample();
			org.lora.biz.workflow.engines.entity.ProcessDefVerExample.Criteria processDefVerCriteria = processDefVerExample
					.createCriteria();
			processDefVerCriteria.andProcessDefinitionIdEqualTo(processDefinitionId);
			processDefVerCriteria.andVersionEqualTo(version);

			ProcessDefVer processDefVer = new ProcessDefVer();
			processDefVer.setResourceId(super.creatBeanId(WorkflowConstant.PREFIX_RESOURCE));
			// 更新resourceId 这个字段
			int count = processDefVerMapper.updateByExampleSelective(processDefVer, processDefVerExample);

			if (count == 0) {
				throw new LoraWorkflowException("saveFlowResourceData update 0 record version=[" + version + "] processDefinitionId=["
						+ processDefinitionId + "]");
			} else if (count > 1) {
				throw new LoraWorkflowException("saveFlowResourceData update more than 1 record version=[" + version
						+ "] processDefinitionId=[" + processDefinitionId + "]");
			}

		}

		// 更新版本表成功后 保存数据到mongodb
		saveMongoData(resourceId, flowData);

	}

	/**
	 * 向mongodb中保存流程定义数据
	 * 
	 * @param resourceId
	 * @param flowData
	 * @throws MongoDBException
	 */
	private void saveMongoData(String resourceId, String flowData) throws LoraCoreException {
		List<Document> documentList = new ArrayList<>();

		Document document = new Document();
		document.append("resourceId", resourceId);
		document.append("flowData", flowData);

		documentList.add(document);

		// 根据resourceId查询 数据库中是否有这个流程定义
		List<Document> list = mongoDAO.find(WorkflowConstant.MONGODB_DATABASE_NAME, WorkflowConstant.MONGODB_WORKFLOW_TABLE_NAME,
				"resourceId", resourceId);

		if (ValidateUtil.isEmpty(list)) {// 插入
			mongoDAO.insert(WorkflowConstant.MONGODB_DATABASE_NAME, WorkflowConstant.MONGODB_WORKFLOW_TABLE_NAME, documentList);
		} else {// 更新

			MongoParamEntity paramEntity = new MongoParamEntity();
			MongoParamEntity updateEntity = new MongoParamEntity();

			paramEntity.setFieldName("resourceId");
			paramEntity.setFieldValue(resourceId);

			updateEntity.setFieldName("flowData");
			updateEntity.setFieldValue(flowData);

			mongoDAO.update(WorkflowConstant.MONGODB_DATABASE_NAME, WorkflowConstant.MONGODB_WORKFLOW_TABLE_NAME, paramEntity, updateEntity);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.lora.biz.workflow.engines.IProcessDefinitionService#getFlowResource
	 * (java.lang.String)
	 */
	@Override
	public String getFlowResource(String resourceId) throws LoraCoreException {

		if (StringUtil.isNull(resourceId)) {
			throw new LoraWorkflowException("resourceId is null");
		}

		List<Document> list = mongoDAO.find(WorkflowConstant.MONGODB_DATABASE_NAME, WorkflowConstant.MONGODB_WORKFLOW_TABLE_NAME,
				"resourceId", resourceId);

		if (ValidateUtil.isEmpty(list)) {
			throw new LoraWorkflowException("getFlowResource empty resourceId=[" + resourceId + "]");
		} else if (list.size() > 1) {
			throw new LoraWorkflowException("getFlowResource return[" + list.size() + "] is too many resourceId=[" + resourceId + "]");
		}

		Document document = list.get(0);

		return document.getString("flowData");

	}

	/**
	 * 根据流程定义ID查询流程定义对象
	 * 
	 * @param processDefinitionId
	 * @return
	 */
	private ProcessDefinition getProcessDefinitionById(String processDefinitionId) {
		ProcessDefinitionExample processDefExample = new ProcessDefinitionExample();
		org.lora.biz.workflow.engines.entity.ProcessDefinitionExample.Criteria processDefCriteria = processDefExample.createCriteria();
		processDefCriteria.andIdEqualTo(processDefinitionId);

		// 获取流程定义
		List<ProcessDefinition> processDefList = processDefinitionMapper.selectByExample(processDefExample);

		if (ValidateUtil.isEmpty(processDefList)) {
			return null;
		}

		return processDefList.get(0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.lora.biz.workflow.engines.IProcessDefinitionService#getProcessDefById
	 * (java.lang.String)
	 */
	@Override
	public ProcessDefinitionBean getProcessDefById(String processDefinitionId) throws LoraCoreException {

		ValidateUtil.isNull(processDefinitionId, "processDefinitionId");

		// 获取流程定义对象
		ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
		if (processDefinition == null) {
			super.businessLog.error("get ProcessDef Fail processDefinitionId=[" + processDefinitionId + "]");
			return null;
		}

		// 流程定义版本列表
		ProcessDefVerExample processDefVerExample = new ProcessDefVerExample();
		org.lora.biz.workflow.engines.entity.ProcessDefVerExample.Criteria processDefVerCriteria = processDefVerExample.createCriteria();
		processDefVerCriteria.andProcessDefinitionIdEqualTo(processDefinitionId);

		List<ProcessDefVer> processDefVerList = processDefVerMapper.selectByExample(processDefVerExample);

		return new ProcessDefinitionBean(processDefinition, processDefVerList);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.biz.workflow.engines.IProcessDefinitionService#
	 * getProcessDefinitionActivity
	 * (org.lora.biz.workflow.engines.ProcessDefinitionEntity, java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public ActivityDefinitionBean getProcessDefinitionActivity(ProcessDefinitionBean processDefinitionEntity, String activityDefId,
			String version) throws LoraCoreException {

		ValidateUtil.isNull(processDefinitionEntity, "ProcessDefinitionEntity");

		ProcessDefVer processDefVer;

		if (StringUtil.isNull(version)) {
			// 版本号为空 取当前生效的版本
			processDefVer = processDefinitionEntity.getEffectiveVer();
		} else {
			// 版本号不为空 取对应的版本
			processDefVer = processDefinitionEntity.getDefinitionVersion(version);
			version = processDefVer.getVersion();
		}

		String processDefName = processDefinitionEntity.getProcessDefinition().getDefinitionName();
		String processDefNumber = processDefinitionEntity.getProcessDefinition().getDefinitionNumber();

		if (processDefVer == null) {
			throw new LoraWorkflowException("get processDefVer Fail processDefName=[" + processDefName + "] processDefNumber=["
					+ processDefNumber + "] version=[" + version + "]");
		}

		// 根据资源ID获取流程定义字符串
		String flowData = getFlowResource(processDefVer.getResourceId());
		super.businessLog.debug("processDefName=[" + processDefName + "] version=[" + version + "] flowData=|"+flowData+"|");

		if (StringUtil.isNull(flowData)) {
			throw new LoraWorkflowException("get flowData Fail processDefName=[" + processDefName + "] processDefNumber=["
					+ processDefNumber + "] version=[" + version + "]");
		}

		// 获取环节定义对象
		return getActivityDefinitionEntity(activityDefId, flowData, processDefinitionEntity, version);

	}

	/**
	 * 获取环节定义对象
	 * 
	 * @param activityDefId
	 * @param flowData
	 * @param processDef
	 * @return
	 * @throws LoraCoreException
	 */
	private ActivityDefinitionBean getActivityDefinitionEntity(String activityDefId, String flowData,
			ProcessDefinitionBean processDefinitionEntity, String version) throws LoraCoreException {

		// 流程定义json对象
		JSONObject flowDataJsonObject = JSONObject.parseObject(flowData);

		// 环节数组json对象
		JSONObject nodeObject = flowDataJsonObject.getJSONObject("nodes");

		// 路径数组json对象
		JSONObject lineObject = flowDataJsonObject.getJSONObject("lines");

		Map<String, ActivityDefinitionBean> activityDefinitionMap = new HashMap<>();

		List<PathDefinitionBean> pathDefinitionList = new ArrayList<>();

		// 根据ID 对环节进行分组
		for (Entry<String, Object> entry : nodeObject.entrySet()) {
			String key = entry.getKey();
			JSONObject node = nodeObject.getJSONObject(key);
			JSONObject dataObj = node.getJSONObject("data");
			ActivityDefinitionBean actDefBean = JSONObject.parseObject(dataObj.toJSONString(), ActivityDefinitionBean.class);
			actDefBean.setId(key);
			actDefBean.setType(node.getString("type"));
			actDefBean.setProcessDefName(processDefinitionEntity.getProcessDefinition().getDefinitionName());
			actDefBean.setProcessDefNo(processDefinitionEntity.getProcessDefinition().getDefinitionNumber());
			actDefBean.setProcessDefVersion(version);
			activityDefinitionMap.put(key, actDefBean);
		}

		// 根据ID 对路径进行分组
		for (Entry<String, Object> entry : lineObject.entrySet()) {
			String key = entry.getKey();
			JSONObject line = lineObject.getJSONObject(key);
			JSONObject dataObj = line.getJSONObject("data");

			PathDefinitionBean pathDefinitionBean = JSONObject.parseObject(dataObj.toJSONString(), PathDefinitionBean.class);
			pathDefinitionBean.setId(key);
			pathDefinitionBean.setFromActivityId(line.getString("from"));
			pathDefinitionBean.setToActivityId(line.getString("to"));
			
			pathDefinitionBean.setToActivityDefinitionBean(activityDefinitionMap.get(line.getString("to")));

			// JSONArray waitActArray = dataObj.getJSONArray("waitAct");
			// List<String> waitActtivityId = new ArrayList<>();
			// for(int i=0;i<waitActArray.size();i++){
			// String activityId = waitActArray.getString(i);
			// waitActtivityId.add(activityId);
			// }
			// pathDefinitionBean.setWaitActtivityId(waitActtivityId);

			pathDefinitionList.add(pathDefinitionBean);

		}

		//创建环节定义对象
		ActivityDefinitionBean bean = creatActivityDefinitionBean(activityDefinitionMap, activityDefId, pathDefinitionList);
		if (bean == null) {
			throw new LoraWorkflowException("get ActivityDefinitionBean Fail  processDefName=["
					+ processDefinitionEntity.getProcessDefinition().getDefinitionName() + "] processDefNumber=["
					+ processDefinitionEntity.getProcessDefinition().getDefinitionNumber() + "] version=[" + version + "]");
		}

		return bean;

	}

	/**
	 * 创建环节定义实体对象
	 * 
	 * @param activityDefinitionMap
	 * @param activityDefId
	 * @param pathDefinitionList
	 * @return
	 * @throws LoraWorkflowException
	 */
	private ActivityDefinitionBean creatActivityDefinitionBean(Map<String, ActivityDefinitionBean> activityDefinitionMap,
			String activityDefId, List<PathDefinitionBean> pathDefinitionList) throws LoraWorkflowException {
		ActivityDefinitionBean result;

		// 获取环节定义
		result = getActivityDefinitionBeanById(activityDefId, activityDefinitionMap, pathDefinitionList);

		if (result == null) {
			return result;
		}

		List<PathDefinitionBean> nextPathList = new ArrayList<>();

		// 迭代寻找后续路径定义
		getNextPath(nextPathList, activityDefinitionMap, pathDefinitionList, result);

		// 添加后续路径 指向的环节信息
		for (PathDefinitionBean pathDefinitionBean : nextPathList) {

			// 路径指向的环节定义ID
			String toActivityId = pathDefinitionBean.getToActivityId();

			if (!activityDefinitionMap.containsKey(toActivityId)) {
				throw new LoraWorkflowException("get activity Fail toActivityId=[" + toActivityId + "] processDefName=["
						+ result.getProcessDefName() + "] processDefNumber=[" + result.getProcessDefNo() + "] version=["
						+ result.getProcessDefVersion() + "]");
			}

			ActivityDefinitionBean nextActivityDefinitionBean = activityDefinitionMap.get(toActivityId);

			pathDefinitionBean.setToActivityDefinitionBean(nextActivityDefinitionBean);

		}

		result.setNextPathList(nextPathList);

		return result;
	}

	/**
	 * 迭代寻找后续路径定义 因为有路由的存在所以迭代
	 * 
	 * @param nextPathList
	 * @param activityDefinitionMap
	 * @param pathDefinitionList
	 * @param result
	 * @throws LoraWorkflowException
	 */
	private void getNextPath(List<PathDefinitionBean> nextPathList, Map<String, ActivityDefinitionBean> activityDefinitionMap,
			List<PathDefinitionBean> pathDefinitionList, ActivityDefinitionBean result) throws LoraWorkflowException {

		for (PathDefinitionBean pathDefinitionBean : pathDefinitionList) {

			if (!ValidateUtil.isEqual(pathDefinitionBean.getFromActivityId(), result.getId())) {
				continue;
			}

			// 路径指向的环节定义ID
			String toActivityId = pathDefinitionBean.getToActivityId();

			if (!activityDefinitionMap.containsKey(toActivityId)) {
				throw new LoraWorkflowException("get activity Fail toActivityId=[" + toActivityId + "] processDefName=["
						+ result.getProcessDefName() + "] processDefNumber=[" + result.getProcessDefNo() + "] version=["
						+ result.getProcessDefVersion() + "]");
			}

			ActivityDefinitionBean nextActivityDefinitionBean = activityDefinitionMap.get(toActivityId);

			if (ValidateUtil.isEqual(WorkflowConstant.ACTIVITY_TYPE_END, nextActivityDefinitionBean.getType())) {
				// 结束环节
				pathDefinitionBean.setToEnd(true);
				nextPathList.add(pathDefinitionBean);
			} else if (ValidateUtil.isEqual(WorkflowConstant.ACTIVITY_TYPE_NORMAL, nextActivityDefinitionBean.getType())) {
				// 普通环节
				pathDefinitionBean.setToEnd(false);
				nextPathList.add(pathDefinitionBean);
			} else if (ValidateUtil.isEqual(WorkflowConstant.ACTIVITY_TYPE_ROUTE, nextActivityDefinitionBean.getType())) {
				// 路由环节
				// 迭代寻找后续路径
				getNextPath(nextPathList, activityDefinitionMap, pathDefinitionList, nextActivityDefinitionBean);
			}

		}

	}

	/**
	 * 根据activityDefId获取环节定义实体
	 * 
	 * @param activityDefId
	 * @param activityDefinitionMap
	 * @param pathDefinitionList
	 * @return
	 * @throws LoraWorkflowException
	 */
	protected ActivityDefinitionBean getActivityDefinitionBeanById(String activityDefId,
			Map<String, ActivityDefinitionBean> activityDefinitionMap, List<PathDefinitionBean> pathDefinitionList)
			throws LoraWorkflowException {

		ActivityDefinitionBean result = null;
		if (StringUtil.isNull(activityDefId)) {// 起草环节

			// 开始环节
			ActivityDefinitionBean startBean = null;

			for (Entry<String, ActivityDefinitionBean> entry : activityDefinitionMap.entrySet()) {
				ActivityDefinitionBean bean = entry.getValue();
				if (ValidateUtil.isEqual(WorkflowConstant.ACTIVITY_TYPE_START, bean.getType())) {
					startBean = bean;
					break;
				}
			}

			if (startBean == null) {
				throw new LoraWorkflowException("getActivityDefinitionBeanById cant find startBean");
			}

			for (PathDefinitionBean pb : pathDefinitionList) {

				if (ValidateUtil.isEqual(startBean.getId(), pb.getFromActivityId())) {
					result = pb.getToActivityDefinitionBean();
					break;
				}

			}

		} else {
			result = activityDefinitionMap.get(activityDefId);
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.biz.workflow.engines.IProcessDefinitionService#
	 * getProcessDefinitionTreeData(java.lang.String)
	 */
	@Override
	public List<Map<String, Object>> getProcessDefinitionTreeData(String domainId) throws LoraCoreException {

		ValidateUtil.isNull(domainId, "domainId");

		List<Map<String, Object>> resultList = new ArrayList<>();

		ProcessDefinitionExample processDefExample = new ProcessDefinitionExample();
		org.lora.biz.workflow.engines.entity.ProcessDefinitionExample.Criteria processDefCriteria = processDefExample.createCriteria();
		if (StringUtil.isNotNull(domainId)) {
			processDefCriteria.andDomainIdEqualTo(domainId);
		}
		// 查询 域下的所有流程定义
		List<ProcessDefinition> processDefList = processDefinitionMapper.selectByExample(processDefExample);

		ProcessDefVerExample processDefVerExample = new ProcessDefVerExample();
		org.lora.biz.workflow.engines.entity.ProcessDefVerExample.Criteria processDefVerCriteria = processDefVerExample.createCriteria();
		processDefVerCriteria.andDomainIdEqualTo(domainId);

		// 获取所有流程定义的版本信息
		List<ProcessDefVer> processDefVerList = processDefVerMapper.selectByExample(processDefVerExample);

		for (ProcessDefinition processDef : processDefList) {

			Map<String, Object> mapProcessDef = new HashMap<>();

			mapProcessDef.put("id", processDef.getId());
			mapProcessDef.put("pId", processDef.getParentId());
			mapProcessDef.put("name", processDef.getDefinitionName());
			mapProcessDef.put("extendData", processDef);

			resultList.add(mapProcessDef);
		}

		for (ProcessDefVer processDefVer : processDefVerList) {
			Map<String, Object> mapProcessDefVer = new HashMap<>();
			String name = processDefVer.getVersion() + ".00";
			if (ValidateUtil.isEqual(processDefVer.getEffective(), WorkflowConstant.STATE_ACTIVATE)) {
				name = name + "[生效]";
			}

			mapProcessDefVer.put("id", processDefVer.getProcessVersionId());
			mapProcessDefVer.put("pId", processDefVer.getProcessDefinitionId());
			mapProcessDefVer.put("name", name);
			mapProcessDefVer.put("resourceId", processDefVer.getResourceId());
			mapProcessDefVer.put("version", processDefVer.getVersion());
			mapProcessDefVer.put("processDefinitionId", processDefVer.getProcessDefinitionId());
			mapProcessDefVer.put("effective", processDefVer.getEffective());

			resultList.add(mapProcessDefVer);
		}

		return resultList;
	}

}
